﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Threading; 

using Microsoft.Practices.Composite.Events;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Enumerations;
using Matchingo.Common.Types.Events;
using Matchingo.Common.Types.Interfaces;
using Matchingo.Common.UIHelpers;

namespace Matchingo.Models.MenuFlowProvider
{
    // NOTE:: may want to consider a queueing system for menu requests, so that clicking on the main menu while going from
    // no menu to the highscore menu after a game will queue it going back to the main menu after it finishes going to the
    // highscore screen
    public class GameMenuFlowProvider : IMenuFlowProvider
    {
        #region ========== Public Properties ==========

        public MenuModuleNames.ModuleNames CurrentMenuItem { get; private set; }

        #endregion

        #region ========== Private Properties ==========

        private IEventAggregator EventAggregator { get; set; }
        private IGameUIContainer GameContainer { get; set; }

        // we have some transitions which go from one menu to another in sequence, during this time the user should not be
        // able to request a different menu, otherwise everything gets all mixed up and the menu becomes unusable
        private bool BlockingAnimationInProgress { get; set; }

        // TODO:: why are there both UI element lists and menu item lists?
        private Dictionary<MenuModuleNames.ModuleNames, UIElement> UIElements { get; set; }
        private Dictionary<MenuModuleNames.ModuleNames, IMenuItem> MenuItems { get; set; }
        private Dictionary<MenuModuleNames.ModuleNames, Point> OriginalPositions { get; set; }

        #endregion

        #region ========== Constructors ==========

        public GameMenuFlowProvider(IEventAggregator eventAggregator, IGameUIContainer gameContainer)
        {
            BlockingAnimationInProgress = false;

            EventAggregator = eventAggregator;
            GameContainer = gameContainer;

            ClearModules();
        }

        #endregion

        #region ========== Public Methods ==========

        public void Initialize()
        {
            // register the menu mask element which is provided by the game container
            RegisterUIModule(null, MenuModuleNames.ModuleNames.Background, GameContainer.MenuMaskingElement);

            // make sure all the needed menu items have been registered
            ValidateAllRequiredModulesRegistered();

            Application.Current.RootVisual.Dispatcher.BeginInvoke(() =>
            {
                // make sure all the menus are out of view
                MenuItems[MenuModuleNames.ModuleNames.OptionsMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, true);
                MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Up, true);
                MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, true);
                MenuItems[MenuModuleNames.ModuleNames.AboutMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Down, true);

                // facebook menu is optional
                if (MenuItems.ContainsKey(MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu))
                {
                    MenuItems[MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, true);
                }
				
				UIElements[MenuModuleNames.ModuleNames.Background].Visibility = Visibility.Collapsed;

                // reset the current item to none
                CurrentMenuItem = MenuModuleNames.ModuleNames.None;

                // notify anyone listening that the menu flow provider has finished initializing
                if (FinishedInitializing != null)
                    FinishedInitializing(this, new EventArgs());
            });
        }

		public void RegisterUIModule(IMenuItem menuItem, MenuModuleNames.ModuleNames key, UIElement e)
        {
            MenuItems.Add(key, menuItem);
            UIElements.Add(key, e);
            OriginalPositions.Add(key, new Point((double)e.GetValue(Canvas.LeftProperty), 
                                                 (double)e.GetValue(Canvas.TopProperty)));
        }

        public void OpenMenu(MenuModuleNames.ModuleNames menuSelection)
        {
            // if there is a blocking animation running then ignore this request
            //if (BlockingAnimationInProgress)
                //return;

			BringMenuToFront(menuSelection);
            EventAggregator.GetEvent<MenuSelectionChangedEvent>().Publish(menuSelection);
        }

        public void CloseMenu()
        {
            // if there is a blocking animation running then ignore this request
            //if (BlockingAnimationInProgress)
                //return;

            OpenMenu(MenuModuleNames.ModuleNames.None);
            EventAggregator.GetEvent<MenuSelectionChangedEvent>().Publish(MenuModuleNames.ModuleNames.None);
        }

        #endregion
        
        #region ========== Public Events ==========

        public event EventHandler FinishedInitializing;        

        #endregion

        #region ========== Private Methods ==========

        private void ValidateAllRequiredModulesRegistered()
        {
            if (!UIElements.ContainsKey(MenuModuleNames.ModuleNames.HighScoreMenu) ||
                !UIElements.ContainsKey(MenuModuleNames.ModuleNames.TitleScreenMenu) ||
                !UIElements.ContainsKey(MenuModuleNames.ModuleNames.OptionsMenu) ||
                !UIElements.ContainsKey(MenuModuleNames.ModuleNames.Background)) // || // the facebook menu should not be required to allow for various builds
				// !UIElements.ContainsKey(MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu))
            {
                throw new ArgumentException("GameMenuFlowProvider - not all required UI elements have been registered");
            }
        }

        private void ClearModules()
        {
            UIElements = new Dictionary<MenuModuleNames.ModuleNames, UIElement>();
            OriginalPositions = new Dictionary<MenuModuleNames.ModuleNames, Point>();
            MenuItems = new Dictionary<MenuModuleNames.ModuleNames, IMenuItem>();
            CurrentMenuItem = MenuModuleNames.ModuleNames.None;
        }

        private void BringMenuToFront(MenuModuleNames.ModuleNames nextMenuItem)
        {
            if (nextMenuItem == MenuModuleNames.ModuleNames.None)
            {
                Application.Current.RootVisual.Dispatcher.BeginInvoke(() =>
                    {
                        // fade out and hide
                        // TODO:: better name element, and perhaps Background module name (isnt it really a menu blocker?)
                        UIElement element = UIElements[MenuModuleNames.ModuleNames.Background];
                        Storyboard sb = TransparencyHelper.BuildTransparencyTransition(element,
                            0.00D, CommonConstants.UI_TRANSITIONS_DEFAULT_MENU_DURATION, false);
                        sb.Completed += (s, e) => { element.Visibility = Visibility.Collapsed; };
                        sb.Begin();
                    });
            }
            else
            {
                Application.Current.RootVisual.Dispatcher.BeginInvoke(() =>
                    {
                        // show and fade in
                        UIElement element = UIElements[MenuModuleNames.ModuleNames.Background];
                        element.Visibility = Visibility.Visible;                        
                        Storyboard sb = TransparencyHelper.BuildTransparencyTransition(element,
                            CommonConstants.UI_TRANSITIONS_DEFAULT_MENU_MASK_OPACITY,
                            CommonConstants.UI_TRANSITIONS_DEFAULT_MENU_DURATION, false);
                        sb.Begin();
                    });
            }

            // switch on what we are currently on
            switch (CurrentMenuItem)
            {
                case MenuModuleNames.ModuleNames.None:

                    switch (nextMenuItem)
                    {
                        case MenuModuleNames.ModuleNames.None:
                            // do nothing
                            break;
                        case MenuModuleNames.ModuleNames.TitleScreenMenu:
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Up, false);
                            break;
                        case MenuModuleNames.ModuleNames.OptionsMenu:
                            // do nothing
                            break;
                        case MenuModuleNames.ModuleNames.HighScoreMenu:
                            DispatcherTimer dtPauseForTwoMenuTransition = new DispatcherTimer();
                            dtPauseForTwoMenuTransition.Interval = new TimeSpan(0, 0, 0, 0, 500); // TODO:: hardcoded timing
                            dtPauseForTwoMenuTransition.Tick += (s2, e2) =>
                            {
                                MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, false);
                                MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Right, false);
                                dtPauseForTwoMenuTransition.Stop();
                                BlockingAnimationInProgress = false;
                            };
                            BlockingAnimationInProgress = true;
                            dtPauseForTwoMenuTransition.Start();
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Up, false);
                            break;
					}
                    break;

                case MenuModuleNames.ModuleNames.TitleScreenMenu:
                    switch (nextMenuItem) // switch on what we are going to
                    {
                        case MenuModuleNames.ModuleNames.None:
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Up, false);
                            break;
                        case MenuModuleNames.ModuleNames.TitleScreenMenu:
                            // do nothing
                            break;
                        case MenuModuleNames.ModuleNames.AboutMenu:
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Up, false);
                            MenuItems[MenuModuleNames.ModuleNames.AboutMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Down, false);
                            break;
                        case MenuModuleNames.ModuleNames.OptionsMenu:
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);
                            MenuItems[MenuModuleNames.ModuleNames.OptionsMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Left, false);
                            break;
                        case MenuModuleNames.ModuleNames.HighScoreMenu:
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, false);
                            MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Right, false);
                            break;
						case MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu:
							MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, false);
							MenuItems[MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Right, false);
							break;
					}
                    break;

                case MenuModuleNames.ModuleNames.AboutMenu:
                    switch (nextMenuItem) // switch on what we are going to
                    {
                        case MenuModuleNames.ModuleNames.TitleScreenMenu:
                            MenuItems[MenuModuleNames.ModuleNames.AboutMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Down, false);
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Up, false);
                            break;
                    }
                    break;

                case MenuModuleNames.ModuleNames.OptionsMenu:
                    switch (nextMenuItem)
                    {
                        case MenuModuleNames.ModuleNames.None:
                            break;
                        case MenuModuleNames.ModuleNames.TitleScreenMenu:
                            MenuItems[MenuModuleNames.ModuleNames.OptionsMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, false);
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Right, false);
                            break;
                        case MenuModuleNames.ModuleNames.OptionsMenu:
                            // do nothing
                            break;
                        case MenuModuleNames.ModuleNames.HighScoreMenu:
                            // do nothing
                            break;
                    }
                    break;

                case MenuModuleNames.ModuleNames.HighScoreMenu:
                    switch (nextMenuItem)
                    {
                        case MenuModuleNames.ModuleNames.None:
                            DispatcherTimer dtPauseForTwoMenuTransition = new DispatcherTimer();
                            dtPauseForTwoMenuTransition.Interval = new TimeSpan(0, 0, 0, 0, 500); // TODO:: hardcoded timing
                            dtPauseForTwoMenuTransition.Tick += (s2, e2) =>
                            {
                                MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Up, false);
                                dtPauseForTwoMenuTransition.Stop();
                                BlockingAnimationInProgress = false;
                            };
                            BlockingAnimationInProgress = true;
                            dtPauseForTwoMenuTransition.Start();
                            MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Left, false);
                            break;
                        case MenuModuleNames.ModuleNames.TitleScreenMenu:
                            MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);
                            MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Left, false);
                            break;
                        case MenuModuleNames.ModuleNames.OptionsMenu:
                            // do nothing
                            break;
                        case MenuModuleNames.ModuleNames.HighScoreMenu:
                            // do nothing
                            break;
						case MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu:
							// First, close the High Score Menu
							MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);

							// Now show the Friends menu
							DispatcherTimer dtPauseForTwoMenuTransitionFB = new DispatcherTimer();
							dtPauseForTwoMenuTransitionFB.Interval = new TimeSpan(0, 0, 0, 0, 500); // TODO:: hardcoded timing
							dtPauseForTwoMenuTransitionFB.Tick += (s2, e2) =>
							{
								MenuItems[MenuModuleNames.ModuleNames.HighScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Left, false);
								MenuItems[MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Right, false);
								dtPauseForTwoMenuTransitionFB.Stop();
								BlockingAnimationInProgress = false;
							};
							BlockingAnimationInProgress = true;
							dtPauseForTwoMenuTransitionFB.Start();
							MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Up, false);
							break;
					}
                    break;
				case MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu:
					switch (nextMenuItem)
					{
						case MenuModuleNames.ModuleNames.None:
							DispatcherTimer dtPauseForTwoMenuTransition = new DispatcherTimer();
							dtPauseForTwoMenuTransition.Interval = new TimeSpan(0, 0, 0, 0, 500); // TODO:: hardcoded timing
							dtPauseForTwoMenuTransition.Tick += (s2, e2) =>
							{
								MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Up, false);
								dtPauseForTwoMenuTransition.Stop();
								BlockingAnimationInProgress = false;
							};
							BlockingAnimationInProgress = true;
							dtPauseForTwoMenuTransition.Start();
							MenuItems[MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);
							MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Left, false);
							break;
						case MenuModuleNames.ModuleNames.TitleScreenMenu:
							MenuItems[MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu].TransitionMenu(InOutEnumeration.InOut.Out, DirectionNames.Direction.Right, false);
							MenuItems[MenuModuleNames.ModuleNames.TitleScreenMenu].TransitionMenu(InOutEnumeration.InOut.In, DirectionNames.Direction.Left, false);
							break;
						case MenuModuleNames.ModuleNames.OptionsMenu:
							// do nothing
							break;
						case MenuModuleNames.ModuleNames.HighScoreMenu:
							// do nothing
							break;
						case MenuModuleNames.ModuleNames.FacebookFriendsScoreMenu:
							// do nothing
							break;
					}
					break;
			} // switch on CurrentMenuItem

            CurrentMenuItem = nextMenuItem; // now set the current item to what we just switched to
        }

        #endregion

    }
}